home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / quicktime / wired movies and sprites / addhtactions / addhtactions.c < prev    next >
Encoding:
Text File  |  2000-06-23  |  15.1 KB  |  547 lines

  1. //////////
  2. //
  3. //    File:        AddHTActions.c
  4. //
  5. //    Contains:    Sample code for adding hypertext links to a QuickTime movie with a text track.
  6. //
  7. //    Written by:    Tim Monroe
  8. //                Based on existing code by Bill Wright
  9. //
  10. //    Copyright:    © 1999 by Apple Computer, Inc., all rights reserved.
  11. //
  12. //    Change History (most recent first):
  13. //
  14. //       <1>         07/19/99    rtm        first file from bw; revised to sample code coding style;
  15. //                                    added Endian macros and other niceties for cross-platform
  16. //                                    support; works fine on Windows and MacOS
  17. //       
  18. //    This file contains some sample code that adds a few wired actions to a movie that has a
  19. //    text track. In particular, it adds two go-to-URL actions to parts of the text track. 
  20. //
  21. //    A text media sample is a 16-bit length word followed by the text of that sample. Optionally,
  22. //    one or more atoms of additional data (called text atom extensions) may follow the text in the
  23. //    sample. The length word specifies the total number of bytes in the text and in any text atom
  24. //    extensions that follow the text. These text atom extensions are organized as "classic" atom
  25. //    structures: a 32-bit length field, followed by a 32-bit type field, followed by the data in 
  26. //    the atom. Here, the length field specifies the total length of the atom (that is, 8 plus the
  27. //    number of bytes in the data.) All the data in the text extension atom must be in big-endian
  28. //    format.
  29. //    
  30. //    To add hypertext actions to a text sample, you simply add a text atom extension of the type
  31. //    kHyperTextTextAtomType to the end of the sample; the data of the text atom extension is the
  32. //    container that holds the information about the actions. So our task boils down to this: find
  33. //    a text sample, create an atom container holding information about the desired actions, and then
  34. //    append a text extension atom whose data is that atom container to the end of the text sample.
  35. //    Then replace the previous text sample with the new one in the text track.
  36. //
  37. //    For complete information about wired actions, see the chapter "Wired Sprites" in the book
  38. //    Programming With QuickTime Sprites.
  39. //
  40. //////////
  41.  
  42. #include "AddHTActions.h"
  43.  
  44.  
  45. //////////
  46. //
  47. // main/WinMain 
  48. // The main function for this application.
  49. //
  50. //////////
  51.  
  52. #if TARGET_OS_MAC
  53. void main (void)
  54. #elif TARGET_OS_WIN32
  55. int CALLBACK WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR theCmdLine, int nCmdShow)
  56. #endif
  57. {
  58.     OSType                    myTypeList[1] = {MovieFileType};
  59.     StandardFileReply        myReply;
  60.     Str255                    myPrompt = "\pSample Hypertext Movie Name:";
  61.     Str255                    myDefaultName = "\pUntitled.mov";
  62.     OSErr                    myErr = noErr;
  63.  
  64. #if TARGET_OS_WIN32
  65.     InitializeQTML(0L);                                            // initialize QuickTime Media Layer
  66. #endif    
  67.  
  68. #if TARGET_OS_MAC
  69.     MaxApplZone();                                                // init everything
  70.     InitGraf(&qd.thePort);
  71.     InitFonts();
  72.     FlushEvents(everyEvent, 0);
  73.     InitWindows();
  74.     InitMenus();
  75.     InitDialogs(NULL);
  76.     TEInit();
  77.     InitCursor();
  78. #endif    
  79.     
  80.     myErr = EnterMovies();
  81.     if (myErr != noErr)
  82.         goto bail;
  83.  
  84.     // elicit a file from the user to save the new movie into
  85.     StandardPutFile(myPrompt, myDefaultName, &myReply);
  86.  
  87.     // create a text movie with hypertext links
  88.     if (myReply.sfGood) {
  89.         myErr = AddHTAct_CreateTextMovie(&myReply.sfFile);
  90.         if (myErr == noErr)
  91.             myErr = AddHTAct_AddHyperTextToTextMovie(&myReply.sfFile);
  92.     }
  93.     
  94. bail:
  95.     ExitMovies();
  96.  
  97. #if TARGET_OS_WIN32
  98.     // terminate the QuickTime Media Layer
  99.     TerminateQTML();
  100.     return(1);
  101. #endif    
  102.  
  103. #if TARGET_OS_MAC
  104.     return;
  105. #endif    
  106. }
  107.  
  108.  
  109. //////////
  110. //
  111. // AddHTAct_CreateTextMovie 
  112. // Create a movie with a text track.
  113. //
  114. //////////
  115.  
  116. static OSErr AddHTAct_CreateTextMovie (FSSpec *theFSSpec)
  117. {
  118.     short            myResRefNum = -1;
  119.     short            myResID = movieInDataForkResID;
  120.     Movie            myMovie = NULL;
  121.     Track            myTrack = NULL;
  122.     Media            myMedia = NULL;
  123.     MediaHandler    myHandler = NULL;
  124.     Rect            myTextBox;
  125.     RGBColor        myTextColor =    {0x6666, 0xCCCC, 0xCCCC};
  126.     RGBColor        myBackColor =    {0x3333, 0x6666, 0x6666};
  127.     RGBColor        myHiliteColor =    {0x0000, 0x0000, 0x0000};
  128.     long            myDisplayFlags = 0;
  129.     short            myHiliteStart = 0;
  130.     short            myHiliteEnd = 0;
  131.     TimeValue        myNewMediaTime;
  132.     TimeValue        myScrollDelay = 0;
  133. #if TARGET_OS_MAC
  134.     char            myText[] = "\rPlease take me to Apple or CNN";
  135. #else
  136.     char            myText[] = "\nPlease take me to Apple or CNN";
  137. #endif
  138.     long            myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile | newMovieActive;
  139.     OSErr            myErr = noErr;
  140.         
  141.     //////////
  142.     //
  143.     // create a new text movie
  144.     //
  145.     //////////
  146.  
  147.     myErr = CreateMovieFile(theFSSpec, FOUR_CHAR_CODE('TVOD'), 0, myFlags, &myResRefNum, &myMovie);
  148.     if (myErr != noErr)
  149.         goto bail;
  150.     
  151.     myTrack = NewMovieTrack(myMovie, FixRatio(kWidth320, 1), FixRatio(kHeight240, 1), kTrackVolumeZero);    
  152.     myMedia = NewTrackMedia(myTrack, TextMediaType, kTimeScale600, NULL, 0);
  153.     if ((myTrack == NULL) || (myMedia == NULL))
  154.         goto bail;
  155.     
  156.     myErr = BeginMediaEdits(myMedia);
  157.     if (myErr != noErr)
  158.         goto bail;
  159.  
  160.     myHandler = GetMediaHandler(myMedia);
  161.     if (myHandler == NULL)
  162.         goto bail;
  163.  
  164.     //////////
  165.     //
  166.     // add a text sample to the movie
  167.     //
  168.     //////////
  169.  
  170.     MacSetRect(&myTextBox, 0, 0, kWidth320, kHeight240);
  171.     MacInsetRect(&myTextBox, kTextBoxInset, kTextBoxInset);
  172.     
  173.     myErr = (OSErr)TextMediaAddTextSample(    myHandler,
  174.                                             myText,
  175.                                             strlen(myText),
  176.                                             kFontIDSymbol,
  177.                                             kSize48,
  178.                                             kFacePlain,
  179.                                             &myTextColor,
  180.                                             &myBackColor,
  181.                                             teCenter,
  182.                                             &myTextBox,
  183.                                             myDisplayFlags,
  184.                                             myScrollDelay,
  185.                                             myHiliteStart,
  186.                                             myHiliteEnd,
  187.                                             &myHiliteColor,
  188.                                             kTimeScale600,
  189.                                             &myNewMediaTime);
  190.     if (myErr != noErr)
  191.         goto bail;
  192.  
  193.     myErr = EndMediaEdits(myMedia);
  194.     if (myErr != noErr)
  195.         goto bail;
  196.         
  197.     // add the media to the track, at time 0
  198.     myErr = InsertMediaIntoTrack(myTrack, kTrackStartTimeZero, myNewMediaTime, kTimeScale600, fixed1);
  199.     if (myErr != noErr)
  200.         goto bail;
  201.     
  202.     // add the movie resource
  203.     myErr = AddMovieResource(myMovie, myResRefNum, &myResID, NULL);
  204.     if (myErr != noErr)
  205.         goto bail;
  206.     
  207. bail:
  208.     if (myResRefNum != -1)
  209.         CloseMovieFile(myResRefNum);
  210.     
  211.     if (myMovie != NULL)
  212.         DisposeMovie(myMovie);
  213.     
  214.     return(myErr);
  215. }
  216.  
  217.  
  218. //////////
  219. //
  220. // AddHTAct_CreateHyperTextActionContainer
  221. // Return, through the theActions parameter, an atom container that contains some hypertext actions.
  222. //
  223. //////////
  224.  
  225. static OSErr AddHTAct_CreateHyperTextActionContainer (QTAtomContainer *theActions)
  226. {
  227.     QTAtom            myEventAtom = 0;
  228.     QTAtom            myActionAtom = 0;
  229.     QTAtom            myHyperTextAtom = 0;
  230.     QTAtom            myWiredObjectsAtom = 0;
  231.     long            myAction;
  232.     long            mySelStart1 = 19;
  233.     long            mySelEnd1 = 24;
  234.     long            mySelStart2 = 28;
  235.     long            mySelEnd2 = 31;
  236.     long            myValue;
  237.     char            myAppleURL[64] = "\pwww.apple.com\0";
  238.     char            myCnnURL[64] = "\pwww.cnn.com\0";
  239.     OSErr            myErr = noErr;
  240.     
  241.     myErr = QTNewAtomContainer(theActions);
  242.     if (myErr != noErr)
  243.         goto bail;
  244.     
  245.     // create a wired objects atom
  246.     myErr = QTInsertChild(*theActions, kParentAtomIsContainer, kTextWiredObjectsAtomType, kIndexOne, kIndexZero, kZeroDataLength, NULL, &myWiredObjectsAtom);
  247.     if (myErr != noErr)
  248.         goto bail;
  249.     
  250.     //////////
  251.     //
  252.     // add a hypertext link to the wired objects atom: ID 1
  253.     //
  254.     //////////
  255.     
  256.     myErr = QTInsertChild(*theActions, myWiredObjectsAtom, kHyperTextItemAtomType, kIDOne, kIndexZero, kZeroDataLength, NULL, &myHyperTextAtom);
  257.     if (myErr != noErr)
  258.         goto bail;
  259.     
  260.     myValue = EndianS32_NtoB(mySelStart1);
  261.     myErr = QTInsertChild(*theActions, myHyperTextAtom, kRangeStart, kIDOne, kIndexZero, sizeof(long), &myValue, NULL);
  262.     if (myErr != noErr)
  263.         goto bail;
  264.     
  265.     myValue = EndianS32_NtoB(mySelEnd1);
  266.     myErr = QTInsertChild(*theActions, myHyperTextAtom, kRangeEnd, kIDOne, kIndexZero, sizeof(long), &myValue, NULL);
  267.     if (myErr != noErr)
  268.         goto bail;
  269.     
  270.     // add an event atom to the hypertext atom;
  271.     // the event type can be any of the five mouse events: down, up, trigger, enter, exit
  272.     myErr = QTInsertChild(*theActions, myHyperTextAtom, kQTEventType, kQTEventMouseClick, kIndexZero, kZeroDataLength, NULL, &myEventAtom);
  273.     if (myErr != noErr)
  274.         goto bail;
  275.     
  276.     myErr = QTInsertChild(*theActions, myEventAtom, kAction, kIndexOne, kIndexOne, kZeroDataLength, NULL, &myActionAtom);
  277.     if (myErr != noErr)
  278.         goto bail;
  279.     
  280.     myAction = EndianS32_NtoB(kActionGoToURL);
  281.     myErr = QTInsertChild(*theActions, myActionAtom, kWhichAction, kIndexOne, kIndexOne, sizeof(long), &myAction, NULL);
  282.     if (myErr != noErr)
  283.         goto bail;
  284.  
  285.     myErr = QTInsertChild(*theActions, myActionAtom, kActionParameter, kIndexOne, kIndexOne, myAppleURL[0] + 1, &myAppleURL[1], NULL);
  286.     if (myErr != noErr)
  287.         goto bail;
  288.         
  289.     //////////
  290.     //
  291.     // add a hypertext link to the wired objects atom: ID 2
  292.     //
  293.     //////////
  294.  
  295.     myErr = QTInsertChild(*theActions, myWiredObjectsAtom, kHyperTextItemAtomType, kIDTwo, kIndexZero, kZeroDataLength, NULL, &myHyperTextAtom);
  296.     if (myErr != noErr)
  297.         goto bail;
  298.     
  299.     myValue = EndianS32_NtoB(mySelStart2);
  300.     myErr = QTInsertChild(*theActions, myHyperTextAtom, kRangeStart, kIDOne, kIndexZero, sizeof(long), &myValue, NULL);
  301.     if (myErr != noErr)
  302.         goto bail;
  303.     
  304.     myValue = EndianS32_NtoB(mySelEnd2);
  305.     myErr = QTInsertChild(*theActions, myHyperTextAtom, kRangeEnd, kIDOne, kIndexZero, sizeof(long), &myValue, NULL);
  306.     if (myErr != noErr)
  307.         goto bail;
  308.     
  309.     // add an event atom to the hypertext atom;
  310.     // the event type can be any of the five mouse events: down, up, trigger, enter, exit
  311.     myErr = QTInsertChild(*theActions, myHyperTextAtom, kQTEventType, kQTEventMouseClick, kIndexZero, kZeroDataLength, NULL, &myEventAtom);
  312.     if (myErr != noErr)
  313.         goto bail;
  314.     
  315.     myErr = QTInsertChild(*theActions, myEventAtom, kAction, kIndexOne, kIndexOne, kZeroDataLength, NULL, &myActionAtom);
  316.     if (myErr != noErr)
  317.         goto bail;
  318.     
  319.     myAction = EndianS32_NtoB(kActionGoToURL);
  320.     myErr = QTInsertChild(*theActions, myActionAtom, kWhichAction, kIndexOne, kIndexOne, sizeof(long), &myAction, NULL);
  321.     if (myErr != noErr)
  322.         goto bail;
  323.  
  324.     myErr = QTInsertChild(*theActions, myActionAtom, kActionParameter, kIndexOne, kIndexOne, myCnnURL[0] + 1, &myCnnURL[1], NULL);
  325.     if (myErr != noErr)
  326.         goto bail;
  327.  
  328. bail:
  329.     return(myErr);
  330. }    
  331.  
  332.  
  333. //////////
  334. //
  335. // AddHTAct_AddHyperActionsToSample
  336. // Add the specified atom container to the end of the specified media sample.
  337. //
  338. // Hypertext actions are stored at the end of the sample as a normal text atom extension.
  339. // In this case, the text atom type is kHyperTextTextAtomType and the data is the container data.
  340. //
  341. //////////
  342.  
  343. static OSErr AddHTAct_AddHyperActionsToSample (Handle theSample, QTAtomContainer theActions)
  344. {
  345.     Ptr            myPtr = NULL;
  346.     long        myHandleLength;
  347.     long        myContainerLength;
  348.     long        myNewLength;        
  349.     OSErr        myErr = noErr;
  350.  
  351.     myHandleLength = GetHandleSize(theSample);
  352.     myContainerLength = GetHandleSize((Handle)theActions);
  353.     
  354.     myNewLength = (long)(sizeof(long) + sizeof(OSType) + myContainerLength);
  355.             
  356.     SetHandleSize(theSample, (myHandleLength + myNewLength));
  357.     myErr = MemError();        
  358.     if (myErr != noErr)
  359.         goto bail;
  360.     
  361.     HLock(theSample);
  362.     
  363.     // get a pointer to the beginning of the new block of space added to the sample
  364.     // by the previous call to SetHandleSize; we need to format that space as a text
  365.     // atom extension
  366.     myPtr = *theSample + myHandleLength;
  367.     
  368.     // set the length of the text atom extension
  369.     *(long *)myPtr = EndianS32_NtoB((long)(sizeof(long) + sizeof(OSType) + myContainerLength));
  370.     myPtr += (sizeof(long));
  371.     
  372.     // set the type of the text atom extension
  373.     *(OSType *)myPtr = EndianS32_NtoB(kHyperTextTextAtomType);
  374.     myPtr += (sizeof(OSType));
  375.     
  376.     // set the data of the text atom extension;
  377.     // we assume that this data is already in big-endian format
  378.     HLock((Handle)theActions);
  379.     BlockMove(*theActions, myPtr, myContainerLength);
  380.     
  381.     HUnlock((Handle)theActions);
  382.     HUnlock(theSample);
  383.  
  384. bail:
  385.     return(myErr);
  386. }
  387.  
  388.  
  389. //////////
  390. //
  391. // AddHTAct_AddHyperTextToTextMovie
  392. // Add some hypertext actions to the specified text movie.
  393. //
  394. //////////
  395.  
  396. static OSErr AddHTAct_AddHyperTextToTextMovie (FSSpec *theFSSpec)
  397. {
  398.     short                            myResID = 0;
  399.     short                            myResRefNum = -1;
  400.     Movie                            myMovie = NULL;
  401.     Track                            myTrack = NULL;
  402.     Media                            myMedia = NULL;
  403.     TimeValue                        myTrackOffset;
  404.     TimeValue                        myMediaTime;
  405.     TimeValue                        mySampleDuration;
  406.     TimeValue                        mySelectionDuration;
  407.     TimeValue                        myNewMediaTime;
  408.     TextDescriptionHandle            myTextDesc = NULL;
  409.     Handle                            mySample = NULL;
  410.     short                            mySampleFlags;
  411.     Fixed                             myTrackEditRate;
  412.     QTAtomContainer                    myActions = NULL;    
  413.     OSErr                            myErr = noErr;
  414.  
  415.     //////////
  416.     //
  417.     // open the movie file and get the first text track from the movie
  418.     //
  419.     //////////
  420.     
  421.     // open the movie file for reading and writing
  422.     myErr = OpenMovieFile(theFSSpec, &myResRefNum, fsRdWrPerm);
  423.     if (myErr != noErr)
  424.         goto bail;
  425.  
  426.     myErr = NewMovieFromFile(&myMovie, myResRefNum, &myResID, NULL, newMovieActive, NULL);
  427.     if (myErr != noErr)
  428.         goto bail;
  429.         
  430.     // find first text track in the movie
  431.     myTrack = GetMovieIndTrackType(myMovie, kIndexOne, TextMediaType, movieTrackMediaType);
  432.     if (myTrack == NULL)
  433.         goto bail;
  434.     
  435.     //////////
  436.     //
  437.     // get first media sample in the text track
  438.     //
  439.     //////////
  440.     
  441.     myMedia = GetTrackMedia(myTrack);
  442.     if (myMedia == NULL)
  443.         goto bail;
  444.     
  445.     myTrackOffset = GetTrackOffset(myTrack);
  446.     myMediaTime = TrackTimeToMediaTime(myTrackOffset, myTrack);
  447.  
  448.     // allocate some storage to hold the sample description for the text track
  449.     myTextDesc = (TextDescriptionHandle)NewHandle(4);
  450.     if (myTextDesc == NULL)
  451.         goto bail;
  452.  
  453.     mySample = NewHandle(0);
  454.     if (mySample == NULL)
  455.         goto bail;
  456.  
  457.     myErr = GetMediaSample(myMedia, mySample, 0, NULL, myMediaTime, NULL, &mySampleDuration, (SampleDescriptionHandle)myTextDesc, NULL, 1, NULL, &mySampleFlags);
  458.     if (myErr != noErr)
  459.         goto bail;
  460.  
  461.     //////////
  462.     //
  463.     // add hypertext actions to the first media sample
  464.     //
  465.     //////////
  466.     
  467.     // create an action container for hypertext actions
  468.     myErr = AddHTAct_CreateHyperTextActionContainer(&myActions);
  469.     if (myErr != noErr)
  470.         goto bail;
  471.     
  472.     // add hypertext actions actions to sample
  473.     myErr = AddHTAct_AddHyperActionsToSample(mySample, myActions);
  474.     if (myErr != noErr)
  475.         goto bail;
  476.         
  477.     //////////
  478.     //
  479.     // replace sample in media
  480.     //
  481.     //////////
  482.     
  483.     myTrackEditRate = GetTrackEditRate(myTrack, myTrackOffset);
  484.     if (GetMoviesError() != noErr)
  485.         goto bail;
  486.  
  487.     GetTrackNextInterestingTime(myTrack, nextTimeMediaSample | nextTimeEdgeOK, myTrackOffset, fixed1, NULL, &mySelectionDuration);
  488.     if (GetMoviesError() != noErr)
  489.         goto bail;
  490.  
  491.     myErr = DeleteTrackSegment(myTrack, myTrackOffset, mySelectionDuration);
  492.     if (myErr != noErr)
  493.         goto bail;
  494.         
  495.     myErr = BeginMediaEdits(myMedia);
  496.     if (myErr != noErr)
  497.         goto bail;
  498.     
  499.     myErr = AddMediaSample(    myMedia,
  500.                             mySample,
  501.                             0,
  502.                             GetHandleSize(mySample),
  503.                             mySampleDuration,
  504.                             (SampleDescriptionHandle)myTextDesc, 
  505.                             1,
  506.                             mySampleFlags,
  507.                             &myNewMediaTime);
  508.     if (myErr != noErr)
  509.         goto bail;
  510.     
  511.     myErr = EndMediaEdits(myMedia);
  512.     if (myErr != noErr)
  513.         goto bail;
  514.     
  515.     // add the media to the track
  516.     myErr = InsertMediaIntoTrack(myTrack, myTrackOffset, myNewMediaTime, mySelectionDuration, myTrackEditRate);
  517.     if (myErr != noErr)
  518.         goto bail;
  519.  
  520.     //////////
  521.     //
  522.     // update the movie resource
  523.     //
  524.     //////////
  525.     
  526.     myErr = UpdateMovieResource(myMovie, myResRefNum, myResID, NULL);
  527.     if (myErr != noErr)
  528.         goto bail;
  529.     
  530.     // close the movie file
  531.     myErr = CloseMovieFile(myResRefNum);
  532.         
  533. bail:
  534.     if (myActions != NULL)
  535.         (void)QTDisposeAtomContainer(myActions);
  536.     
  537.     if (mySample != NULL)
  538.         DisposeHandle(mySample);        
  539.     
  540.     if (myTextDesc != NULL)
  541.         DisposeHandle((Handle)myTextDesc);        
  542.     
  543.     if (myMovie != NULL)
  544.         DisposeMovie(myMovie);    
  545.  
  546.     return(myErr);
  547. }